package com.data.center.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Snowflake;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.data.center.dictionary.Status;
import com.data.center.dto.OrganizationDto;
import com.data.center.dto.StaffDto;
import com.data.center.entity.Organization;
import com.data.center.entity.Staff;
import com.data.center.exception.exception.BizException;
import com.data.center.exception.info.CommonEnum;
import com.data.center.mapper.OrganizationMapper;
import com.data.center.mapper.StaffMapper;
import com.data.center.service.OrganizationService;
import com.data.center.service.StaffService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.data.center.util.FieldDataManagementUtils;
import com.data.center.util.ForeignManage.ForeignDataItem;
import com.data.center.util.ForeignManage.ForeignDataUtil;
import com.data.center.util.ForeignManage.ForeignManageUtils;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * <p>
 *  业务人员服务实现类
 * </p>
 *
 * @author 
 * @since 2023-12-05 03:29:24
 */
@Service
@Slf4j
public class StaffServiceImpl extends ServiceImpl<StaffMapper, Staff> implements StaffService {

    @Resource
    private StaffMapper staffMapper;
    @Resource
    private OrganizationMapper organizationMapper;
    @Autowired
    private OrganizationService organizationService;
    @Autowired
    private ForeignManageUtils foreignManageUtils;
    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private ForeignDataUtil foreignDataUtil;

    private Snowflake snowflake = new Snowflake();
    private Map<String, String> K = new HashMap<>(); // 输入参数中key-数据查询键名称MAP

    public StaffServiceImpl() {
        K.put("mobile", "mobile");
        K.put("telephone", "telephone");
        K.put("email", "email");
        K.put("fax", "fax");
    }

    /**
     * @Description:检查ID是否以S开头
     * */
    public boolean staffIdVerify(StaffDto staff) {
        return staff.getStaffId().indexOf("S") == 0;
    }

    /**
     * @Description:规范化ID
     * */
    public void staffIdStandardize(StaffDto staff) {
        if (StringUtil.isEmpty(staff.getStaffId())) {
            staffIdSetDefault(staff); // 生成ID
        } else {
            String result = "S" + staff.getStaffId();
            if (result.length() >= 32) {
                staff.setStaffId(result.substring(0, 32));// ID长度最多32，裁剪
            }
        }
    }

    /**
     * @Description:设置默认ID
     * */
    public void staffIdSetDefault(StaffDto staff) {
        String result = "S" + snowflake.nextIdStr();
        staff.setStaffId(result);
    }

    /**
     * @Description:检查姓名是否为空/符合要求
     * @没有对应的标准化方法，无法验证的数据就要抛出异常
     * */
    public boolean nameVerify(StaffDto staffDto) {
        if (StringUtil.isEmpty(staffDto.getName())) {
            throw new BizException("业务人员姓名不允许为空");
        }

        // 中文格式
        Pattern pattern = Pattern.compile("^[\u4e00-\u9fa5·]+$");
        Matcher matcher = pattern.matcher(staffDto.getName());
        if (matcher.matches()) {
            return true;
        }
        // 西文格式
        pattern = Pattern.compile("^[A-Za-z\\-' ]+$");
        matcher = pattern.matcher(staffDto.getName());
        if (matcher.matches()) {
            return true;
        }
        throw new BizException("业务人员姓名为不允许的格式");
    }

    /**
     * @Description:检查组织ID是否为已存在外键
     * */
    public void nameStandardize(StaffDto staffDto) {
        staffDto.setName(staffDto.getName().replace(" ", ""));
    }

    /**
     * @Description:检查组织ID是否为已存在外键
     * */
    public boolean organizationIdVerify(StaffDto staff) {
        if (organizationMapper.findById(staff.getOrganizationId()) == null) {
            return false;
        }
        return true;
    }

    /**
     * @Description:标准化组织ID，将单位名称通过自动查询标准化为外键
     * */
    public void organizationIdStandardize(StaffDto staff) {
        List<OrganizationDto> findOrg = organizationService.findOrganization(staff.getOrganizationId(), null);
        if (findOrg == null || findOrg.size() != 1) {
            throw new BizException("工作单位无法自动标注化，请先入库工作单位组织");
        } else {
            staff.setOrganizationId(findOrg.get(0).getOrganizationId());;
        }
    }

    /**
     * 标准化Staff对象
     * @param staff StaffDto对象
     * @return 标准化后的Staff对象
     * @throws InvocationTargetException 抛出目标方法调用时发生的异常
     * @throws IllegalAccessException 抛出访问字段或方法发生错误时的异常
     */
    public StaffDto standardizeStaff(StaffDto staff) throws InvocationTargetException, IllegalAccessException {
        for (Field field: Staff.class.getDeclaredFields()) {
            if (StringUtil.isNotEmpty((String) BeanUtil.getFieldValue(staff, field.getName()))) { // 只校验/标准化有数据的键

                if (!FieldDataManagementUtils.verify(
                        this,
                        StaffServiceImpl.class,
                        field.getName(),
                        staff,
                        StaffDto.class)
                ) {
                    FieldDataManagementUtils.standardize(
                            this,
                            StaffServiceImpl.class,
                            field.getName(),
                            staff,
                            StaffDto.class
                    ); // 检验不通过自动尝试标准化
                }
            }
        }
        return staff;
    }

    /**
     * 查找符合要求的业务人员信息
     *
     * @param staffDto 人员信息
     * @return 人员信息链表
     */
    @Override
    public List<StaffDto> findStaff(StaffDto staffDto) {
        if (StringUtil.isEmpty(staffDto.getName())) {
            throw new BizException("查询业务人员姓名不允许为空"); // S-M1
        }

        // 检查姓名是否规范
        nameStandardize(staffDto);

        // S-M2
        Set<Staff> cachestaffList = new HashSet<>();

        // 此处采用拼接OR连接SQL语句的方式
        // 构造查询参数
        QueryWrapper<Staff> param = new QueryWrapper<>();
        param.eq("name", staffDto.getName());
        boolean hasOtherVerify = false;
        for (String key: K.keySet()) {
            if (StringUtil.isNotEmpty((String) BeanUtil.getFieldValue(staffDto, key))) { // 输入参数中不为空，则进行与姓名的组合查询
                hasOtherVerify = true;
                param.eq(K.get(key), BeanUtil.getFieldValue(staffDto, key));
            }
        }
        cachestaffList.addAll(staffMapper.selectList(param)); // 将查询的结果存入HashSet，通过ID去重
//        if (!hasOtherVerify) { // 如果没有任何K中判断，则退化为查询名称
//            cachestaffList.addAll(staffMapper.selectList(param)); // 将查询的结果存入HashSet，通过ID去重
//        }

        // 至多查询得到|K|个人才
        if (cachestaffList.size() > K.size()) {
            throw new BizException("库中存在数据破坏了S-M3原则" + cachestaffList);
        }

        return BeanUtil.copyToList(cachestaffList, StaffDto.class); // 转换为Dto链表
    }

    /**
     * 添加业务人员
     *
     * @param staff 业务人员的DTO对象
     * @return 添加的业务人员对象
     * @throws java.lang.reflect.InvocationTargetException
     * @throws java.lang.IllegalAccessException
     */
    @Override
    public Staff addStaff(StaffDto staff) throws InvocationTargetException, IllegalAccessException {
        if (StringUtil.isEmpty(staff.getName())) {
            throw new BizException("业务人员姓名不允许为空"); // S-M1
        }

        // S-M2
        boolean unSafe = true;
        for (String key: K.keySet()) {
            if (StringUtil.isNotEmpty((String) BeanUtil.getFieldValue(staff, key))) { // 输入参数中不为空，则进行与姓名的组合查询
                unSafe = false;
            }
        }
        if (unSafe) {
            throw new BizException("业务人员信息不完整,违反S-M2");
        }

        try {
            staff = standardizeStaff(staff); // 标准化，如果出错则抛出异常
        } catch (InvocationTargetException invocationTargetException) {
            throw new BizException("标准化staff对象失败" + invocationTargetException.getTargetException());
        } catch(IllegalAccessException illegalAccessException) {
            throw new BizException(illegalAccessException.toString());
        }

        List<StaffDto> findResult = findStaff(staff);
        if (findResult == null || findResult.size() == 0) {
            staffIdSetDefault(staff); // 生成ID
            staff.setCreator("default"); // TODO:设置创建者
            staff.setCreationTime(new Date()); // 设置创建时间
            staffMapper.insert(BeanUtil.copyProperties(staff, Staff.class)); // 插入数据库
        } else {
            throw new BizException("库中已经存在数据，插入将破坏S-M3原则");
        }

        return BeanUtil.copyProperties(staff, Staff.class);
    }

    /**
     * 强行添加业务人员
     * @param staff 业务人员的DTO对象
     * @return 返回添加完成的业务人员对象
     * @throws InvocationTargetException 当调用目标方法时抛出异常
     * @throws IllegalAccessException 当访问或修改类的成员变量或方法时抛出异常
     */
    @Override
    public Staff forceAddStaff(StaffDto staff) throws InvocationTargetException, IllegalAccessException {
        if (StringUtil.isEmpty(staff.getName())) {
            throw new BizException("业务人员姓名不允许为空"); // S-M1
        }

        // S-M2
        boolean unSafe = true;
        for (String key: K.keySet()) {
            if (StringUtil.isNotEmpty((String) BeanUtil.getFieldValue(staff, key))) { // 输入参数中不为空，则进行与姓名的组合查询
                unSafe = false;
            }
        }
        if (unSafe) {
            throw new BizException("业务人员信息不完整,违反S-M2");
        }


        try {
            staff = standardizeStaff(staff); // 标准化，如果出错则抛出异常
        } catch (InvocationTargetException invocationTargetException) {
            throw new BizException("标准化staff对象失败" + invocationTargetException.getTargetException());
        } catch(IllegalAccessException illegalAccessException) {
            throw new BizException(illegalAccessException.toString());
        }

        List<StaffDto> findResult = findStaff(staff);
        if (findResult == null || findResult.size() == 0) {
            // 执行插入
            staffIdSetDefault(staff); // 生成ID
            staff.setCreator("default"); // TODO:设置创建者
            staff.setCreationTime(new Date()); // 设置创建时间
            staffMapper.insert(BeanUtil.copyProperties(staff, Staff.class)); // 插入数据库
        } else if (findResult.size() >= 1){
            // 循环遍历，精准校验S-M3，发现任何一个业务人员K中存在与输入相同的元素，则抛出异常，否则执行插入
            for (StaffDto staffDto:
                    findResult) {
                for (String key: K.keySet()) {
                    if (BeanUtil.getFieldValue(staff, key).equals(BeanUtil.getFieldValue(staffDto, key))) {
                        throw new BizException("库中已经存在数据，插入将破坏S-M3原则");
                    }
                }
            }

            // 执行插入
            staffIdSetDefault(staff); // 生成ID
            staff.setCreator("default"); // TODO:设置创建者
            staff.setCreationTime(new Date()); // 设置创建时间
            staffMapper.insert(BeanUtil.copyProperties(staff, Staff.class)); // 插入数据库
        }

        return BeanUtil.copyProperties(staff, Staff.class);
    }

    /**
     * 更新业务人员信息
     * @param originalStaff 原始业务人员信息
     * @param updateStaff 更新的业务人员信息
     * @return 更新后的业务人员信息
     * @throws InvocationTargetException 当方法调用目标抛出异常时抛出
     * @throws IllegalAccessException 当访问权限冲突时抛出
     */
    @Override
    public StaffDto updateStaff(StaffDto originalStaff, StaffDto updateStaff) throws InvocationTargetException, IllegalAccessException {

        // S-M1
        if (StringUtil.isEmpty(updateStaff.getName())) {
            throw new BizException("业务人员姓名不能为空");
        }

        StaffDto standardizeStaff; // 这里不进行标准化后对象为null的判断，而是直接将标准化过程中抛出的异常链式抛出至表示层统一返回
        try {
            standardizeStaff = standardizeStaff(updateStaff); // 标准化，如果出错则抛出异常
        } catch (InvocationTargetException invocationTargetException) {
            throw new BizException("标准化staff对象失败" + invocationTargetException.getTargetException());
        } catch(IllegalAccessException illegalAccessException) {
            throw new BizException(illegalAccessException.toString());
        }

        // 更新K+name信息指定更新键的对应值,用于后续判断S-M2与S-M3原则
        K.put("name", "name");
        StaffDto updateKValue = new StaffDto();
        for (String key: K.keySet()) { // 设置姓名 + 其余四要素
            if (BeanUtil.getFieldValue(standardizeStaff, key)!= null) {
                BeanUtil.setFieldValue(updateKValue, key, BeanUtil.getFieldValue(standardizeStaff, key));
            }
        }
        K.remove("name");
        // 判断是否不符合S-M2原则
        boolean unSafe = true;
        for (String key: K.keySet()) {
            if (StringUtil.isNotEmpty((String) BeanUtil.getFieldValue(updateKValue, key))) {
                unSafe = false;
            }
        }
        if (unSafe) {
            throw new BizException("业务人员信息不完整,违反S-M2");
        }

        List<StaffDto> findResult = findStaff(updateKValue);
        Set<StaffDto> findResultSet = new HashSet<>();
        findResultSet.addAll(findResult);
        findResultSet.remove(originalStaff);
        if (findResultSet.size() != 0) {
            throw new BizException("库中已经存在数据，更新将破坏S-M3原则"); // S-M3
        } else {
            // 执行更新
            standardizeStaff.setModifier("DefaultUpdater"); // TODO:设置修改者
            standardizeStaff.setLastModifiedTime(new Date()); // 设置修改时间
            for (Object key: updateStaff.getKeyWithAdditionalInformation()) {
                if (BeanUtil.getFieldValue(standardizeStaff, key.toString())!= null) {
                    BeanUtil.setFieldValue(originalStaff, key.toString(), BeanUtil.getFieldValue(standardizeStaff, key.toString()));
                }
            }
            staffMapper.updateById(BeanUtil.copyProperties(originalStaff, Staff.class)); // 更新数据库
            return originalStaff;
        }
    }

    /**
     * 逻辑删除
     * 假删除，将数据状态设置为离职
     * @Param id
     */
    @Override
    public boolean logicalDelete(String id) {
        Staff staff = staffMapper.selectById(id);
        try {
            staff.setStatus(Status.RETIRED.getKey()); // 默认设置成离退状态
            staff.setModifier("DefaultDeleter"); // TODO:设置实际修改者
            staff.setLastModifiedTime(new Date());
            staffMapper.updateById(staff);
        } catch (Exception e) {
            throw new BizException(CommonEnum.DELETE_FALSE);
        }

        return true;
    }

    /**
     * @description:物理删除
     * 物理删除，将数据从数据库中删除
     */
    @Override
    public boolean physicalDelete(String id) throws InvocationTargetException, IllegalAccessException {

        List<String> errorList = new ArrayList<>(); // 记录错误信息
        // 获取所有依赖本表的表
        Staff staff = staffMapper.selectById(id);
        if (staff == null) {
            throw new BizException(CommonEnum.DELETE_FALSE);
        }
        int count = 0;
        for (ForeignDataItem item : foreignManageUtils.getForeignDataItem(Staff.class.getName())) {
            List list = (List) item.searchByForeignKeyMethod.invoke(
                    applicationContext.getBean((Class) item.serviceClass),
                    BeanUtil.getFieldValue(staff, item.foreignKey)




            );
            errorList.addAll(list);
            count += list.size();
        }

        if (count > 0) {
            log.error("物理删除业务人员" + id + "失败，存在依赖：" + errorList);
            throw new BizException(CommonEnum.DELETE_FALSE);
        }

        staffMapper.deleteById(id);
        return true;
    }

    /**
     * 合并员工信息
     * @param staffDto 待合并的员工对象
     * @param deletedStuff 被删除的员工对象
     * @return 合并后的员工对象
     * @throws InvocationTargetException 当调用方法时抛出的异常
     * @throws IllegalAccessException 当访问类或方法时抛出的异常
     */
    @Override
    public StaffDto merge(StaffDto staffDto, StaffDto deletedStuff) throws InvocationTargetException, IllegalAccessException {

        // 获取 deletedStuff 对象的 staffId 值
        String deletedStuffStaffId = deletedStuff.getStaffId();

        Staff existingStaff = staffMapper.selectById(deletedStuffStaffId);

        if (existingStaff == null) {
            throw new BizException(CommonEnum.MERGE_FALSE);
        }

        // 检查依赖
        foreignDataUtil.mergeByForeignKey(staffDto, deletedStuff);

        // 依赖清理结束后，删除数据
        staffMapper.deleteById(deletedStuff.getStaffId());
        return staffDto;
    }


    /**
     * @Description:组织ID外键依赖服务查询方法
     * */
    @Override
    public List<Staff> searchByOrganizationId(String id) {
        // 构造查询参数
        Map<String, Object> queryParams = new HashMap<>();
        queryParams.put("organization_id", id);
        return staffMapper.selectByMap(queryParams);
    }
    /**
     * @Description:组织ID外键依赖服务删除方法
     * */
    @Override
    public int deleteByOrganizationId(String id) {
        // 构造查询参数
        Map<String, Object> queryParams = new HashMap<>();
        queryParams.put("organization_id", id);
        staffMapper.deleteByMap(queryParams);
        return 1;
    }

    /**
     * @description:组织ID外键上的合并方法，将组织ID为deletedId的数据的组织ID修改为id
     * @param id
     * @param deletedId
     * @return
     */
    @Override
    public int mergeByOrganizationId(String id, String deletedId) {
        log.info("mergeByForeignKey参数" + id + "  " + deletedId );
        Map<String, Object> queryParams = new HashMap<>();
        queryParams.put("organization_id", deletedId);
        List<Staff> deletedstaffs = staffMapper.selectByMap(queryParams);
        for (Staff mergeItem : deletedstaffs) {
            mergeItem.setOrganizationId(id);
            staffMapper.updateById(mergeItem);
        }
        return 1;
    }


}
